#include "MainWindow.h"

#include <cmath>

#include "CompoundPropertiesDialog.h"
#include "AlloyPropertiesDialog.h"
#include "LayerPropertiesDialog.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setMinimumSize(800, 600);

    initialize();

    //    setCentralWidget(/*createCentralWidget()*/);

    custom_plot = new QCustomPlot;
    custom_plot->setMinimumSize(QSize(640,480));
    custom_plot->clearGraphs();

    tabela->addTab(createCompoundWidget(), "Związki");
    tabela->addTab(createAlloysWidget(), "Stopy");

    QWidget* design = createCentralWidget();
    //    qDebug() << Q_FUNC_INFO << 1;
    tabela->addTab(design, "Projektowanie");

    //    qDebug() << Q_FUNC_INFO << 2;
    connect(execute_button, SIGNAL(clicked()), this, SLOT(startWortInAThread()));
    //connect(gaas,SIGNAL(),this,SLOT(oblicz_ww()));

    connect(compound_add_button, SIGNAL(clicked()),
            this, SLOT(compound_add()));
    connect(compound_LW, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
            this, SLOT(compound_edit(QListWidgetItem *)));
    connect(compound_edit_button, SIGNAL(clicked()),
            this, SLOT(compound_edit()));

    connect(alloys_LW, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
            this, SLOT(alloy_edit(QListWidgetItem *)));
    connect(alloys_edit_button, SIGNAL(clicked()),
            this, SLOT(alloy_edit()));
    connect(alloys_add_button, SIGNAL(clicked()),
            this, SLOT(alloy_add()));

    connect(layer_LW, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
            this, SLOT(layer_edit(QListWidgetItem *)));
    connect(layer_edit_button, SIGNAL(clicked()),
            this, SLOT(layer_edit()));
    connect(layer_add_button, SIGNAL(clicked()),
            this, SLOT(layer_add()));
    connect(layer_remove_button,SIGNAL(clicked()),
            this,SLOT(layer_remove()));

    QHBoxLayout * mainLayout = new QHBoxLayout();
    mainLayout->addWidget(tabela,0 , Qt::AlignLeft);
    mainLayout->addWidget(custom_plot, 1, Qt::AlignLeft);
    mainLayout->addWidget(log_TE,0,Qt::AlignRight);

    QWidget * centralWidget = new QWidget();
    centralWidget->setLayout(mainLayout);

    setCentralWidget(centralWidget);

    QMenuBar * menubar = new QMenuBar();
    QMenu * fileMenu = menubar->addMenu("&Plik");
    fileMenu->addAction("Wyjści&e", this, SLOT(close()), QKeySequence("Ctrl+x"));

    setMenuBar(menubar);

    QStatusBar * statusBar = new QStatusBar();
    statusBar->addWidget(new QLabel("Postęp"), 0);
    statusBar->addWidget(calculationProgressBar, 1);

    setStatusBar(statusBar);

    Compound InSb;
    InSb.name = "InSb";
    InSb.Eg =  0.235;
    InSb.a_lc = 6.4794;
    InSb.delta_so = 0.81;
    InSb.mass_ef = 0.0135;
    InSb.vbo = 0.0;
    InSb.ac = -6.94;
    InSb.av = -0.36;
    InSb.b = -2.0;
    InSb.c11 = 684.7;
    InSb.c12 = 373.5;
    QListWidgetItem* InSb_item = new QListWidgetItem();
    QVariant in_v;
    in_v.setValue(InSb);
    InSb_item->setData(Qt::UserRole, in_v);
    InSb_item->setData(Qt::DisplayRole, InSb.name);
    compound_LW->addItem(InSb_item);

    Compound GaSb;
    GaSb.name = "GaSb";
    GaSb.Eg =  0.812;
    GaSb.a_lc = 6.0959;
    GaSb.delta_so = 0.76;
    GaSb.mass_ef = 0.039;
    GaSb.vbo = -0.03;
    GaSb.ac = -7.5;
    GaSb.av = -0.8;
    GaSb.b = -2.0;
    GaSb.c11 = 884.2;
    GaSb.c12 = 402.6;
    QListWidgetItem* GaSb_item = new QListWidgetItem();
    QVariant ga_v;
    ga_v.setValue(GaSb);
    GaSb_item->setData(Qt::UserRole, ga_v);
    GaSb_item->setData(Qt::DisplayRole, GaSb.name);
    compound_LW->addItem(GaSb_item);

    Alloy GaInSb;
    GaInSb.name = "GaxIn(1-x)Sb";
    GaInSb.Eg_bow =  0.415;
    GaInSb.a_lc_bow = 0;
    GaInSb.delta_so_bow = 0.1;
    GaInSb.mass_ef_bow = 0.0092;
    GaInSb.vbo_bow = 0.0;
    GaInSb.ac_bow = 0.0;
    GaInSb.av_bow = 0.0;
    GaInSb.b_bow = 0.0;
    GaInSb.c11_bow = 0.0;
    GaInSb.c12_bow = 0.0;

    GaInSb.firstCompoundIndex = compound_LW->indexAt(QPoint(0,0));
    GaInSb.secondCompoundIndex = compound_LW->indexAt(QPoint(0,0));
    QListWidgetItem* GaInSb_item = new QListWidgetItem();
    QVariant gain_v;
    gain_v.setValue(GaInSb);
    GaInSb_item->setData(Qt::UserRole, gain_v);
    GaInSb_item->setData(Qt::DisplayRole, GaInSb.name);
    alloys_LW->addItem(GaInSb_item);
}

MainWindow::~MainWindow(){}

void MainWindow::initialize(){

    ilosc_podzialow_n_LE = new QLineEdit("100");
    sigma_LE = new QLineEdit("1e-5");
    execute_button = new QPushButton("Wykonaj");
    ilosc_podzialow_n_label = new QLabel("ilość podziałów");
    sigma_label = new QLabel("Bład");
    tabela = new QTabWidget();
    compound_LW = new QListWidget();
    alloys_LW = new QListWidget();
    layer_LW = new QListWidget();
    log_TE = new QTextEdit();

    compound_edit_button = new QPushButton("Edytuj");
    compound_add_button = new QPushButton("Dodaj");

    alloys_edit_button = new QPushButton("Edytuj");
    alloys_add_button = new QPushButton("Dodaj");

    layer_add_button = new QPushButton("Dodaj warstwę");
    layer_edit_button = new QPushButton("Edytuj");
    layer_remove_button = new QPushButton("Usuń");

    calculationProgressBar = new QProgressBar();
    calculationProgressBar->setRange(0, 100);

    connect(this, SIGNAL(notifyCalculationProgress(int)),
            calculationProgressBar, SLOT(setValue(int)));

    statusBar()->setEnabled(false);
}

QWidget * MainWindow::createCompoundWidget()
{
    QVBoxLayout* compound_layout = new QVBoxLayout();
    compound_layout->addWidget(compound_LW);
    QHBoxLayout * compound_button_layout = new QHBoxLayout();
    compound_button_layout->addWidget(compound_edit_button);
    compound_button_layout->addWidget(compound_add_button);
    compound_layout->addLayout(compound_button_layout);

    QWidget * compound_widget = new QWidget();
    compound_widget->setLayout(compound_layout);

    return compound_widget;
}
QWidget * MainWindow::createCentralWidget()
{
    QVBoxLayout* v_layout2 = new QVBoxLayout();
    v_layout2->addWidget(ilosc_podzialow_n_label);
    v_layout2->addWidget(ilosc_podzialow_n_LE);
    QVBoxLayout* v_layout3 = new QVBoxLayout();
    v_layout3->addWidget(sigma_label);
    v_layout3->addWidget(sigma_LE);

    QHBoxLayout * h_layout1  = new QHBoxLayout();
    h_layout1->addLayout(v_layout2);
    h_layout1->addLayout(v_layout3);

    QVBoxLayout* v_layout = new QVBoxLayout();
    v_layout->addLayout(h_layout1);
    v_layout->addWidget(execute_button);

    QHBoxLayout* h_layout2 = new QHBoxLayout();
    h_layout2->addLayout(v_layout);
    QHBoxLayout* lyer_button_layout = new QHBoxLayout();
    lyer_button_layout->addWidget(layer_edit_button);
    lyer_button_layout->addWidget(layer_remove_button);

    QVBoxLayout * main_v_layout = new QVBoxLayout();
    main_v_layout->addWidget(layer_LW);
    main_v_layout->addWidget(layer_add_button);
    main_v_layout->addLayout(lyer_button_layout);
    main_v_layout->addLayout(h_layout2);

    QWidget* central_widget = new QWidget();
    central_widget->setLayout(main_v_layout);

    return central_widget;
}
QWidget * MainWindow::createAlloysWidget()
{
    QLayout * alloy_layout = new QVBoxLayout();
    alloy_layout->addWidget(alloys_LW);
    QLayout * buttonLayout = new QHBoxLayout();
    buttonLayout->addWidget(alloys_edit_button);
    buttonLayout->addWidget(alloys_add_button);
    alloy_layout->addItem(buttonLayout);

    QWidget * alloy_widget = new QWidget();
    alloy_widget->setLayout(alloy_layout);

    return alloy_widget;
}
QWidget * MainWindow::createDesignWidget()
{
    QWidget * designWidget = new QWidget();
    return designWidget;
}

void MainWindow::startWortInAThread(){
    m_finished_CB = false;
    m_finished_VBHh = false;
    m_finished_VBLh = false;
    qDebug() << Q_FUNC_INFO;
    //    custom_plot->clearGraphs();
    if( layer_LW->count() != 0){
        Input input_CB, input_VB_lh, input_VB_hh;
        double size;

        input_CB = createInputCB(size);
        input_VB_hh = createInputVBHh(size);
        input_VB_lh = createInputVBLh(size);
        //qDebug() << size;
        statusBar()->setEnabled(true);

        Worker * worker_CB = new Worker();
        Worker * worker_VB_hh = new Worker();
        Worker * worker_VB_lh = new Worker();

        worker_CB->setRozmiar(size);
        worker_CB->setSigma(sigma_LE->text().toDouble());
        worker_CB->setPartitions(ilosc_podzialow_n_LE->text().toInt());
        worker_CB->setInput(input_CB);
        worker_CB->setVB(false);

        connect(worker_CB, SIGNAL(finished()),
                this, SLOT(fin_CB()));
        connect(worker_CB, SIGNAL(finished()),
                this, SLOT(plot()));

        connect(worker_CB, &Worker::notifyProgress,
                calculationProgressBar, &QProgressBar::setValue);
        connect(worker_CB, &Worker::finished,
                worker_CB, &QObject::deleteLater);

        connect(worker_CB, SIGNAL(ready(Result)),
                this, SLOT(handleResult(Result)));
        worker_CB->start();

        worker_VB_hh->setRozmiar(size);
        worker_VB_hh->setSigma(sigma_LE->text().toDouble());
        worker_VB_hh->setPartitions(ilosc_podzialow_n_LE->text().toInt());
        worker_VB_hh->setInput(input_VB_hh);
        worker_VB_hh->setVB(true);

        connect(worker_VB_hh, SIGNAL(finished()),
                this, SLOT(fin_VBHh()));
        connect(worker_VB_hh, SIGNAL(finished()),
                this, SLOT(plot()));
        connect(worker_VB_hh, &Worker::notifyProgress,
                calculationProgressBar, &QProgressBar::setValue);
        connect(worker_VB_hh, &Worker::finished,
                worker_VB_hh, &QObject::deleteLater);

        connect(worker_VB_hh, SIGNAL(ready(Result)),
                this, SLOT(handleResult(Result)));
        worker_VB_hh->start();

        worker_VB_lh->setRozmiar(size);
        worker_VB_lh->setSigma(sigma_LE->text().toDouble());
        worker_VB_lh->setPartitions(ilosc_podzialow_n_LE->text().toInt());
        worker_VB_lh->setInput(input_VB_lh);
        worker_VB_lh->setVB(true);
        connect(worker_VB_lh, &Worker::notifyProgress,
                calculationProgressBar, &QProgressBar::setValue);
        connect(worker_VB_lh, &Worker::finished,
                worker_VB_lh, &QObject::deleteLater);

        connect(worker_VB_lh, SIGNAL(ready(Result)),
                this, SLOT(handleResult(Result)));
        connect(worker_VB_lh, SIGNAL(finished()),
                this, SLOT(fin_VBLh()));
        connect(worker_VB_lh, SIGNAL(finished()),
                this, SLOT(plot()));
        worker_VB_lh->start();
        qDebug() << Q_FUNC_INFO;


    }
}
Input MainWindow::createInputCB(double & size){
    Input input;
    QListWidgetItem* item;
    QVector<double> vec_x, vec_y,vec_mass;
    int n;
    bool succes_n;
    n = ilosc_podzialow_n_LE->text().toInt(&succes_n);
    qDebug() << Q_FUNC_INFO;
    double skok, d_max;

    size = 0;

    for ( int i = 0; i < layer_LW->count(); ++i){
        item = layer_LW->item(i);
        size += item->data(Qt::UserRole).value<Layer>().d;
    }
    if (succes_n){
        skok = size/n;
        for(double i = 0; i < size; i += skok){
            vec_x.push_back(i);
        }
        //    rozmiar_warstwy = 0;
        for ( int i = 0; i < layer_LW->count(); ++i){
            item = layer_LW->item(i);
            d_max = 0;
            while(d_max <= item->data(Qt::UserRole).value<Layer>().d){
                vec_y.push_back(item->data(Qt::UserRole).value<Layer>().cb());
                vec_mass.push_back(item->data(Qt::UserRole).value<Layer>().mass_ef());
                d_max += skok;
            }
        }
    }
    input.vec_v = vec_y;
    m_graphData.push_back(vec_y);
    m_vecX = vec_x;
    input.vec_mass = vec_mass;
    qDebug() << Q_FUNC_INFO;
    return input;
}
Input MainWindow::createInputVBHh(double & size){
    Input input;
    QListWidgetItem* item;
    QVector<double> vec_y,vec_mass;
    int n;
    bool succes_n;
    n = ilosc_podzialow_n_LE->text().toInt(&succes_n);
    qDebug() << Q_FUNC_INFO;
    double skok, d_max;

    size = 0;

    for ( int i = 0; i < layer_LW->count(); ++i){
        item = layer_LW->item(i);
        size += item->data(Qt::UserRole).value<Layer>().d;
    }
    //    qDebug() << rozmiar;

    skok = size/n;
    //    rozmiar_warstwy = 0;
    for ( int i = 0; i < layer_LW->count(); ++i){
        item = layer_LW->item(i);
        d_max = 0;
        while(d_max <= item->data(Qt::UserRole).value<Layer>().d){
            vec_y.push_back(-1*item->data(Qt::UserRole).value<Layer>().hh());
            //qDebug() << item->data(Qt::UserRole).value<Layer>().hh();
            vec_mass.push_back(item->data(Qt::UserRole).value<Layer>().mass_ef());
            d_max += skok;
        }
    }

    input.vec_v = vec_y;
    m_graphData.push_back(vec_y);
    input.vec_mass = vec_mass;
    qDebug() << Q_FUNC_INFO;
    return input;
}
Input MainWindow::createInputVBLh(double & size){
    Input input;
    QListWidgetItem* item;
    QVector<double> vec_y,vec_mass;
    int n;
    bool succes_n;
    n = ilosc_podzialow_n_LE->text().toInt(&succes_n);
    qDebug() << Q_FUNC_INFO;
    double skok, d_max;

    size = 0;

    for ( int i = 0; i < layer_LW->count(); ++i){
        item = layer_LW->item(i);
        size += item->data(Qt::UserRole).value<Layer>().d;
    }

    skok = size/n;

    for ( int i = 0; i < layer_LW->count(); ++i){
        item = layer_LW->item(i);
        d_max = 0;
        while(d_max <= item->data(Qt::UserRole).value<Layer>().d){
            vec_y.push_back(-1*item->data(Qt::UserRole).value<Layer>().lh());
            //qDebug() << item->data(Qt::UserRole).value<Layer>().lh();
            vec_mass.push_back(item->data(Qt::UserRole).value<Layer>().mass_ef());
            d_max += skok;
        }
    }

    input.vec_v = vec_y;
    m_graphData.push_back(vec_y);
    input.vec_mass = vec_mass;
    qDebug() << Q_FUNC_INFO;
    return input;
}

inline void minmax(const QVector<double>& vec, double& min, double& max){
    foreach (double x, vec){
        if (x > max)
            max = x;
        if (x < min)
            min = x;
    }
}
QVector<double> reverse(QVector<double> vec){
    for (int i = 0; i < vec.size(); ++i){
        vec[i] *= -1.0;
    }
    return vec;
}

const double PLOT_RANGE_ADJUSTMENT = 0.01;

void MainWindow::plot(){
    if(m_finished_CB && m_finished_VBHh && m_finished_VBLh){
        QString str;
        qDebug() << Q_FUNC_INFO;
        custom_plot->clearGraphs();
        double x_min = 1e30,x_max = -1e30, y_min = 1e30, y_max = -1e30;
        minmax(m_vecX,x_min,x_max);
        for (int i = 0; i < m_graphData.size() ; ++i){
            //        qDebug() << i << m_graphData.size();

            if ( i > 0 ){
                QVector<double> temp_vec = reverse(m_graphData.at(i));
                custom_plot->addGraph();
                custom_plot->graph(i)->addData(m_vecX,temp_vec);
                custom_plot->graph(i)->setPen(QPen(Qt::red));
                minmax(temp_vec,y_min,y_max);
                //            qDebug() << i;
            } else {
                custom_plot->addGraph();
                custom_plot->graph(i)->addData(m_vecX,m_graphData.at(i));
                custom_plot->graph(i)->setPen(QPen(Qt::red));
                minmax(m_graphData.at(i),y_min,y_max);
                //            qDebug() << i;
            }
        }
        QVector<double> temp_x, temp_y,energy_levels;
        for (int i = 0; i < m_results.records.size(); ++i){
            //        qDebug() << i << m_results.records.size();
            temp_x.clear();
            temp_y.clear();
            temp_x.push_back(m_vecX.at(0));
            temp_x.push_back(m_vecX.at(m_vecX.size()-1));
            //        qDebug() << m_vecX.at(m_vecX.size()-1);
            temp_y.push_back(m_results.records.at(i).energyLevel);
            temp_y.push_back(m_results.records.at(i).energyLevel);
            energy_levels.push_back(m_results.records.at(i).energyLevel);

//                    qDebug() << str;
            custom_plot->addGraph();
            custom_plot->graph(i+3)->addData(temp_x,temp_y);
            //        qDebug() << i + 2;
        }
        qSort(energy_levels);
        str += "E\tdeltaE\n";
        for(int i = 0; i< energy_levels.size(); ++i){
            if (i > 0 ){
                str += QString::number(energy_levels.at(i)) + "\t" +QString::number(energy_levels.at(i) - energy_levels.at(i-1)) + "\n";
            }else
                str += QString::number(energy_levels.at(i)) + "\t" +QString::number(energy_levels.at(i)) + "\n";
        }
        custom_plot->xAxis->setRange(x_min - PLOT_RANGE_ADJUSTMENT,x_max + PLOT_RANGE_ADJUSTMENT);
        custom_plot->yAxis->setRange(y_min - PLOT_RANGE_ADJUSTMENT,y_max + PLOT_RANGE_ADJUSTMENT);
        custom_plot->replot();
        m_vecX.clear();
        m_graphData.clear();
        m_results.records.clear();
        log_TE->setPlainText(str);
        qDebug() << Q_FUNC_INFO;
    }
}



void MainWindow::handleResult(Result result){
    statusBar()->setEnabled(false);
    m_results.addData(result);
}
void MainWindow::fin_CB(){
    m_finished_CB = true;
}

void MainWindow::fin_VBHh(){
    m_finished_VBHh = true;
}

void MainWindow::fin_VBLh(){
    m_finished_VBLh =  true;
}

void MainWindow::compound_add(){

    QListWidgetItem * item = new QListWidgetItem();
    CompoundPropertiesDialog add_widget(item, this);

    if (QDialog::Accepted == add_widget.exec()){
        compound_LW->addItem(item);
    } else {
        delete item;
    }
}

void MainWindow::compound_edit(){
    if (compound_LW->currentItem() != NULL ){
        QListWidgetItem* item;
        item = compound_LW->currentItem();

        CompoundPropertiesDialog edit_widget(item, this);
        edit_widget.exec();
    }
}

void MainWindow::compound_edit(QListWidgetItem * item){
    CompoundPropertiesDialog edit_widget(item, this);
    edit_widget.exec();
}

void MainWindow::alloy_add(){
    QListWidgetItem * item = new QListWidgetItem();
    AlloyPropertiesDialog add_widget(item, compound_LW->model(), this);

    if (QDialog::Accepted == add_widget.exec()){
        alloys_LW->addItem(item);
    } else {
        delete item;
    }
}

void MainWindow::alloy_edit(){
    if (alloys_LW->currentItem() != NULL ){
        QListWidgetItem* item;
        item = alloys_LW->currentItem();

        AlloyPropertiesDialog edit_widget(item, compound_LW->model(), this);
        edit_widget.exec();
    }
}

void MainWindow::alloy_edit(QListWidgetItem * item){
    AlloyPropertiesDialog edit_widget(item, compound_LW->model(), this);
    edit_widget.exec();
}

void MainWindow::layer_add(){
    QListWidgetItem * item = new QListWidgetItem();
    double a;
    if(layer_LW->count()!= 0){
        a = layer_LW->item(0)->data(Qt::UserRole).value<Layer>().a_lc();
    } else
        a = 0.0;
    qDebug() << a;
    LayerPropertiesDialog add_widget(item, alloys_LW->model(),a, this);

    if (QDialog::Accepted == add_widget.exec()){
        layer_LW->addItem(item);
    } else {
        delete item;
    }

}

void MainWindow::layer_edit(){
    if (layer_LW->currentItem() != NULL ){
        QListWidgetItem* item;
        item = layer_LW->currentItem();

        LayerPropertiesDialog edit_widget(item, alloys_LW->model(),layer_LW->currentIndex().data(Qt::UserRole).value<Layer>().a_base, this);
        edit_widget.exec();
    }

}

void MainWindow::layer_edit(QListWidgetItem * item){
    LayerPropertiesDialog edit_widget(item, alloys_LW->model(),layer_LW->currentIndex().data(Qt::UserRole).value<Layer>().a_base, this);
    edit_widget.exec();
}
void MainWindow::layer_remove(){
    if(layer_LW->currentItem() != NULL){
        int row = layer_LW->currentRow();
        layer_LW->takeItem(row);
    }
}

